package com.example.console.impl;

import com.example.console.Command;
import com.example.console.anno.ConsoleCommand;
import com.example.console.exception.ArgumentException;
import com.example.console.exception.CommandException;
import com.example.console.exception.ExecuteException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * @author frank
 */
public class MethodCommand implements Command {

    private final String name;
    private final String description;
    private final Object target;
    private final Method method;
    private final Class<?>[] types;
    private ObjectMapper mapper;

    public MethodCommand(Object target, Method method, ObjectMapper mapper) {
        this.target = target;
        this.mapper = mapper;
        method.setAccessible(true);
        this.method = method;
        ConsoleCommand annotation = method.getAnnotation(ConsoleCommand.class);
        this.name = annotation.name().trim();
        this.description = annotation.description().trim();
        this.types = method.getParameterTypes();
    }

    @Override
    public void execute(String[] arguments) throws CommandException {
        if (arguments.length != types.length) {
            throw new ArgumentException("指令参数长度不正确");
        }

        Object[] args = new Object[arguments.length];
        try {
            for (int i = 0; i < arguments.length; i++) {
                Type type = types[i];
                if (type.equals(String.class)) {
                    args[i] = arguments[i];
                } else {
                    args[i] = mapper.readValue(arguments[i], types[i]);
                }
            }
        } catch (Exception e) {
            throw new ArgumentException("参数转换时出现异常", e);
        }

        try {
            method.invoke(target, args);
        } catch (Exception e) {
            throw new ExecuteException("方法执行时出现异常", e);
        }
    }

    @Override
    public String name() {
        return name;
    }

    @Override
    public String description() {
        return description;
    }
}
